#include<iostream>
#include <iomanip>
#include<cmath>
#include"EquationSolver.hpp"
#include"Function.h"
using namespace std;
const double Pi = acos(-1.);
const double e = exp(1.0);
class Fb1 : public Function
{
public:
    double operator() (double x) const 
    {
        return 1/x - tan(x);
    }
};
class Fb2 : public Function
{
public:
    double operator() (double x) const
    {
        return 1/x-pow(2,x);
    }
};
class Fb3 : public Function
{
public:
    double operator() (double x) const
    {
        return pow(2,-x)+pow(e,x)+2*cos(x)-6;
    }
};
class Fb4 : public Function
{
public:
    double operator() (double x) const
    {
        return (pow(x, 3) + 4 * pow(x, 2) + 3 * x + 5)/(2*pow(x,3)-9*pow(x,2)+18*x-2);
    }
};
void solve_fb1()
{
    std::cout << "Solving x^{-1} -tanx on [0,pi/2]" << endl;
    Bisection_Method solver_fb(Fb1(), 0, Pi/2);
    double x = solver_fb.solve();
    cout << "A root is :" << x << endl;

}
void solve_fb2()
{
    std::cout << "Solving x^{-1} -2^{x} on [0,1]" << endl;
    Bisection_Method solver_fb(Fb2(), 0, 1);
    double x = solver_fb.solve();
    cout << "A root is :" << x << endl;

}
void solve_fb3()
{
    std::cout << "Solving 2^{-x} +e^{x}+2cosx -6 on [1,3]" << endl;
    Bisection_Method solver_fb(Fb3(), 1, 3);
    double x = solver_fb.solve();
    cout << "A root is :" << x << endl;
  
}
void solve_fb4()
{
    std::cout << "Solving (x^(3)+4*x^(2)+3x+5)/(2*x^(3)-9*x^(2)+18x-2)on [0,4]" << endl;
    Bisection_Method solver_fb(Fb4(), 0, 4);
    double x = solver_fb.solve();
    cout << "A root is :" << x << endl;

}

void solve_b() 
{
    solve_fb1();
    solve_fb2();
    solve_fb3();
    solve_fb4();

}
class Fc : public Function
{
public:
    double operator() (double x)const 
    {
        return x-tan(x);
    }
    double derivative(double x) const 
    {
        return 1-1/pow(cos(x),2);
    }
};
void solve_c() 
{
    cout << "Solving x= tanx  near 4.5or 7.7" << endl;
    Newton_Method solver_fc1(Fc(), 4.5);
    double x = solver_fc1.solve();
    Newton_Method solver_fc2(Fc(), 7.7);
    double y = solver_fc2.solve();
    cout << "the roots near 4.5,7.7 respectively are " << x <<' '<<y << endl;
}
class Fd1 : public Function
{
public:
    double operator() (double x) const
    {
        return sin(x/2)-1;
    }
};
class Fd2 : public Function
{
public:
    double operator() (double x) const
    {
        return pow(e,x)-tan(x);
    }
};
class Fd3 : public Function
{
public:
    double operator() (double x) const
    {
        return pow(x,3)-12*pow(x,2)+3*x+1;
    }
};


void solve_d()
{
    Secant_Method solver_fd1(Fd1(), 0, Pi / 2);
    double x1 = solver_fd1.solve();
    cout << "the root of sin(x / 2) - 1 with x0 = 0, x1 = pi / 2: "<<" "<<x1 << endl;
    Secant_Method solver_fd2(Fd2(), 1, 1.4);
    double x2 = solver_fd2.solve();
    cout << "the root of e^{x}-tanx with x0=1, x1=1.4 : " << " " << x2 << endl;
    Secant_Method solver_fd3(Fd3(), 0, -0.5);
    double x3 = solver_fd3.solve();
    cout << "the root of x^{3}-12*x^{2}+3x+1 with x0=0, x1=-0.5 : " << " " << x3 << endl;

}
class Fe : public Function
{
public:
    double operator() (double x) const
    {
        return asin(x)+x*pow(1-x*x,1/2)+1.24-0.5*Pi;

    }
    double derivative(double x) const
    {
        return 1/sqrt(1-x*x)+pow(1-x*x,1/2)-x*x/ pow(1 - x * x, 1 / 2);
    }

};
void solve_e()
{
    Bisection_Method solver_feb(Fe(), 0, 1);
    double xb = solver_feb.solve();
    cout <<"the root of bisection method :" << fixed << setprecision(2) << xb << endl;
    Newton_Method solver_fen(Fe(), 0.155);
    double xn = solver_fen.solve();
    cout << "the root of newton method :" << fixed << setprecision(2) << xn<< endl;
   Secant_Method solver_fes(Fe(), 0.1, 0.2);
    double xs = solver_fes.solve();
    cout << "the root of secant method :" << fixed << setprecision(2) << xs << endl;

}
class Ff: public Function {
private:
    double A, B, C, E;
public:
    Ff(double A, double B, double C, double E) : A(A), B(B), C(C), E(E) {}

    virtual double operator() (double alpha) const {
        return A * std::sin(alpha) * std::cos(alpha) + B * std::pow(std::sin(alpha), 2) -
            C * std::cos(alpha) - E * std::sin(alpha);
    }

    virtual double derivative(double alpha) const {
        return A * (std::cos(2 * alpha)) + 2 * B * std::sin(alpha) * std::cos(alpha) +
            C * std::sin(alpha) - E * std::cos(alpha);
    }
};

void solve_f() {
    // Part (a) 使用牛顿法验证 α ≈ 33°
    double l = 89.0;
    double h = 49.0;
    double D = 55.0;
    double beta1 = 11.5 * Pi / 180.0;  

    double A = l * std::sin(beta1);
    double B = l * std::cos(beta1);
    double C = (h + 0.5 * D) * std::sin(beta1) - 0.5 * D * std::tan(beta1);
    double E = (h + 0.5 * D) * std::cos(beta1) - 0.5 * D;

    Ff func(A, B, C, E);
    Newton_Method newton_solver(func, 33.0 * Pi / 180.0);  // 初始猜测 α = 33°
    double alpha = newton_solver.solve();
    std::cout << "Part (a): α ≈ " << alpha * 180.0 / Pi << "°" << std::endl;

    // Part (b) 使用牛顿法求解 α (D = 30)
    D = 30.0;
    C = (h + 0.5 * D) * std::sin(beta1) - 0.5 * D * std::tan(beta1);
    E = (h + 0.5 * D) * std::cos(beta1) - 0.5 * D;

    Ff func2(A, B, C, E);
    Newton_Method newton_solver2(func2, 33.0 * Pi / 180.0);  // 使用相同初值
    alpha = newton_solver2.solve();
    std::cout << "Part (b): α ≈ " << alpha * 180.0 / Pi << "°" << std::endl;

    // Part (c) 使用割线法求解 α，初始值远离 33°
    Secant_Method secant_solver(func2, 20.0 * Pi / 180.0, 150.0 * Pi/ 180.0);
    alpha = secant_solver.solve();
    std::cout << "Part (c): α ≈ " << alpha * 180.0 / Pi << "°" << std::endl;

}
int main()
{
    cout << "Proble b" << endl;
    solve_b();
    cout << "Proble c" << endl;
    solve_c();
    cout << "Proble d" << endl;
    solve_d();
    cout << "Proble e" << endl;
    solve_e();
    cout << "Proble f" << endl;
    solve_f();
    return 0;
}